package jlib.component.tool {

	import jlib.component.tool.DragSelectBox;
	import jlib.component.tool.ISelectableTool;
	import jlib.component.tool.ITool;
	import jlib.component.tool.catchers.CatcherState;
	import jlib.component.tool.catchers.ICatcher;
	import jlib.component.tool.catchers.NomalCatcher;
	import jlib.component.tool.event.MultiToolEvent;
	import jlib.component.tool.event.SelectToolEvent;
	import jlib.component.tool.event.TransformHandlerEvent;
	import jlib.component.tool.factory.DefaultMultiToolFactory;
	import jlib.component.tool.factory.IMultiToolFactory;
	import jlib.component.tool.handler.AbstractTransformHandler;
	import jlib.component.tool.skin.points.AbstractControlPoint;
	
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;

	/**
	 * SelectTool과 TransformTool 이 같이 움직이는 툴
	 * v1.8
	 * @author 박종희
	 */
	public final class MultiTool extends EventDispatcher implements ITool, ISelectableTool {
		
		private var m_outline:Sprite;
		private var m_catcher:ICatcher;
		private var m_handler:AbstractTransformHandler;
		
		private var m_selectBox:DragSelectBox;
		private var m_canvas:DisplayObjectContainer;
		private var m_stage:Stage;
		
		private var m_prevMatrixDatas:Array;
		private var m_transformedMatrixData:Array;
		private var m_matrixList:Vector.<Matrix>;
		
		/**
		 * @example <listing version="3.1" >
		 * var multiTool:MultiTool = new MultiTool();
		 * multiTool.setCanvas(canvas);
		 * multiTool.on();
		 * </listing>
		 * @param multiToolFactory tool의 기본적인 요소를 생성하는 팩토리
		 */
		public function MultiTool($factory:IMultiToolFactory=null): void
		{
			init($factory);
		}
		
		public function init($factory:IMultiToolFactory): void
		{
			m_matrixList = new Vector.<Matrix>();
			
			if(!$factory) $factory = new DefaultMultiToolFactory();
			
			m_catcher = $factory.createCatcher();
			m_selectBox = $factory.createSelectBox();
			setOutline($factory.createTransformOutline());
			setHandler($factory.createTransformHandler());
			
			m_catcher.addEventListener(SelectToolEvent.CHANGE_SELECTLIST, dispatch);
		}
		
		public function getOutline(): Sprite { return m_outline; }
		
		public function setOutline($outline:Sprite): void
		{
			m_outline = $outline;	
		}
		
		public function getHandler(): AbstractTransformHandler { return m_handler; }
		
		public function setHandler($handler:AbstractTransformHandler): void
		{
			if(m_handler) m_handler.removeEventListener(TransformHandlerEvent.CHANGED_TRANSFORM, dispatch);
			m_handler = $handler;
			m_handler.addEventListener(TransformHandlerEvent.CHANGED_TRANSFORM, dispatch);
			if(m_canvas) setCanvas(m_canvas);
			
		}
		
		public function getCatcher(): ICatcher { return  m_catcher; }
		
		public function setCatcher($catcher:ICatcher): void
		{
			if(m_catcher) m_catcher.removeEventListener(SelectToolEvent.CHANGE_SELECTLIST, dispatch);
			m_catcher = $catcher;
			m_catcher.addEventListener(SelectToolEvent.CHANGE_SELECTLIST, dispatch);
			if(m_canvas) setCanvas(m_canvas);
		}
		
		private function press(event:MouseEvent): void
		{
			m_catcher.press(event);
			
			if(m_catcher.getMode() == CatcherState.CATCH_MULTI) {
				m_outline.visible = false;
				m_selectBox.press(event);
				m_catcher.reset();
			}else {
				m_outline.visible = true;
				if( m_catcher.getMode() == CatcherState.CATCHED_OVERLAP ){
					if(m_catcher.isEmptyCatchList()) return;
					dispatchEvent(new MultiToolEvent(MultiToolEvent.START_TRANSFORM));
					m_handler.press(event);
				}else if(m_catcher.getMode() == CatcherState.CATCHED_NEWSINGLE || m_catcher.getMode() == CatcherState.CATCH_SINGLE ) {
					if(m_catcher.isEmptyCatchList()){
						m_outline.visible = false;
						return;
					}
					m_handler.setTargets(m_catcher.getCatchedList());
					dispatchEvent(new MultiToolEvent(MultiToolEvent.START_TRANSFORM));
					m_handler.press(event);
				}else if(m_catcher.getMode() == CatcherState.CATCHED_ANOTHER) {
					if(event.target is AbstractControlPoint){
						dispatchEvent(new MultiToolEvent(MultiToolEvent.START_TRANSFORM));
						m_handler.press(event);
					} else {
						m_outline.visible = false;
					}
				}
			}
			m_stage.addEventListener(MouseEvent.MOUSE_MOVE, move );
			m_stage.addEventListener(MouseEvent.MOUSE_UP, release );
		}
		
		private function move(event:MouseEvent): void
		{
			if( m_catcher.getMode() == CatcherState.CATCH_MULTI ) {
				m_catcher.move(event);
				m_selectBox.move(event);
			}else if( m_catcher.getMode() != CatcherState.CATCHED_ANOTHER ){
				m_handler.move(event);
			}else {
				if(m_outline.visible && !m_catcher.isEmptyCatchList()){
					m_handler.move(event);
				}
			}
			event.updateAfterEvent();
		}
		
		private function release(event:MouseEvent): void
		{
			if( m_catcher.getMode() == CatcherState.CATCH_MULTI ){
				m_catcher.release(event)
				m_selectBox.release(event);
				if(!m_catcher.isEmptyCatchList()){
					m_prevMatrixDatas = m_transformedMatrixData = null;
					m_outline.visible = true;
					m_handler.setTargets(m_catcher.getCatchedList());
				}
			} else if(m_catcher.getMode() != CatcherState.CATCH_MULTI && m_outline.visible){
				if(!m_catcher.isEmptyCatchList()){
					m_handler.release(event);
					m_handler.apply();
				}
			}
			
			m_stage.removeEventListener( MouseEvent.MOUSE_MOVE , move );
			m_stage.removeEventListener( MouseEvent.MOUSE_UP , release );
		}
		
		/** 이벤트 렙핑 */
		private function dispatch(e:Object): void { 
			dispatchEvent(new MultiToolEvent(e.type, e.notify)); 
		}
		
		/** 도큐멘트 셋팅 */
		public function setCanvas($canvas:DisplayObjectContainer ): void
		{
			m_canvas = $canvas;
			if(!m_canvas.parent) throw new Error('canvas.parent 값이 null 입니다. canvas 를 addChild 해주십쇼.')
			
			m_stage = m_canvas.stage;
			
			m_catcher.setCanvas(m_canvas);
			m_catcher.setStage(m_stage);
			m_selectBox.setTarget(m_canvas);
			
			m_handler.setCanvas(m_canvas);//2
			m_handler.setOutline(m_outline);//3
			
			m_canvas.parent.addChild(m_selectBox);
			m_canvas.parent.addChild(m_outline);
		}
		
		/** 초기화 후 값 갱신 */
		public function update(e:Event = null): void
		{
			if(m_catcher.isEmptyCatchList()) return;
			m_handler.update(e);
		}
		
		/** 툴 리셋 */
		public function reset(e:Event=null): void
		{
			m_catcher.reset();
			//m_handler.setVisible(false);
		}
		/** 툴을 on */
		public function on() : void
		{
			if(m_stage) m_stage.addEventListener( MouseEvent.MOUSE_DOWN, press );
		}
		/** 툴을 off */
		public function off() : void
		{
			reset();
			if(m_stage) m_stage.removeEventListener( MouseEvent.MOUSE_DOWN, press );
		}
		/** resetCatchedList */
		public function resetSelectedList():void { this.m_catcher.reset(); }
		
		/**
		 * tool 에 select 된 객체들을 반환합니다. 
		 * @return displayObject들 배열.
		 */		
		public function getSelectList(): Array { return m_catcher.getCatchedList(); }
		/**
		 * tool에 selectList를 만듭니다.
		 * @param $list selectlist 
		 */		
		public function setSelectList($list:Array): void  { m_catcher.setCatchedList($list); }
		
	}//c
}

